/*
 *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

package com.uenpay.im.ui.old.activity.videochat;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.FragmentTransaction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.qyx.android.database.DBTalkMsgColumns;
import com.qyx.android.database.TalkMsgManager;
import com.qyx.android.message.type.MsgContentType;
import com.qyx.android.message.type.MsgType;
import com.qyx.android.message.type.PacketType;
import com.qyx.android.protocol.QyxMsg;
import com.qyx.android.protocol.SessionModel;
import com.uenpay.im.BroadcastAction;
import com.uenpay.im.IMApplication;
import com.uenpay.im.R;
import com.uenpay.im.callback.IOnBottomDialogListener;
import com.uenpay.im.configuration.APIConfiguration;
import com.uenpay.im.entities.chatbean.MsgCancelCallVideoModel;
import com.uenpay.im.weight.DialogUtility;
import com.uenpay.im.weight.RoundImageView;
import com.qyx.videochat.handle.AppRTCAudioManager;
import com.qyx.videochat.handle.AppRTCClient;
import com.qyx.videochat.handle.AppRTCClient.RoomConnectionParameters;
import com.qyx.videochat.handle.AppRTCClient.SignalingParameters;
import com.qyx.videochat.handle.PeerConnectionClient;
import com.qyx.videochat.handle.PeerConnectionClient.PeerConnectionParameters;
import com.qyx.videochat.handle.UnhandledExceptionHandler;
import com.qyx.videochat.handle.WebSocketRTCClient;
import com.qyx.videochat.util.ExtraVideoConstant;
import com.qyx.videochat.util.LooperExecutor;
import com.qyx.videochat.util.VideoChatResultType;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.IceCandidate;
import org.webrtc.RendererCommon.ScalingType;
import org.webrtc.SessionDescription;
import org.webrtc.StatsReport;
import org.webrtc.VideoRenderer;
import org.webrtc.VideoRendererGui;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Activity for peer connection call setup, call waiting and call view.
 */
public class CallActivity extends Activity implements
		AppRTCClient.SignalingEvents,
		PeerConnectionClient.PeerConnectionEvents, CallFragment.OnCallEvents {
	private int hour, minute, second;
	private QyxMsg mQyxMsg = new QyxMsg();
	private RelativeLayout top_view;
	private RoundImageView call_avatar_iv;
	private TextView call_name_tv, call_time_tv;
	private MediaPlayer BackgroundMediaPlayer;
	private TalkMsgManager mTalkMsgManager = new TalkMsgManager();
	private RefuseVideoChatBroadcastReceiver mRefuseVideoChatBroadcastReceiver;
	private static final String TAG = "CallRTCClient";
	private PeerConnectionClient peerConnectionClient = null;
	private AppRTCClient appRtcClient;
	private SignalingParameters signalingParameters;
	private AppRTCAudioManager audioManager = null;
	private VideoRenderer.Callbacks localRender;
	private VideoRenderer.Callbacks remoteRender;
	private ScalingType scalingType;
	private Toast logToast;
	private boolean commandLineRun;
	private int runTimeMs;
	private boolean activityRunning;
	private RoomConnectionParameters roomConnectionParameters;
	private PeerConnectionParameters peerConnectionParameters;
	private boolean iceConnected;
	private boolean isError;
	private boolean callControlFragmentVisible = true;
	private long callStartedTimeMs = 0;
	private Timer callTimer = null;
	private int talk_time = 0;
	// Controls
	private GLSurfaceView videoView;
	CallFragment callFragment;
	HudFragment hudFragment;

	@SuppressLint("NewApi")
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		IMApplication.getInstance().addActivity(this);
		Thread.setDefaultUncaughtExceptionHandler(new UnhandledExceptionHandler(
				this));

		// Set window styles for fullscreen-window size. Needs to be done before
		// adding content.
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().addFlags(
				LayoutParams.FLAG_FULLSCREEN | LayoutParams.FLAG_KEEP_SCREEN_ON
						| LayoutParams.FLAG_DISMISS_KEYGUARD
						| LayoutParams.FLAG_SHOW_WHEN_LOCKED
						| LayoutParams.FLAG_TURN_SCREEN_ON);
		getWindow().getDecorView().setSystemUiVisibility(
				View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
						| View.SYSTEM_UI_FLAG_FULLSCREEN
						| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
		setContentView(R.layout.activity_call);
		iceConnected = false;
		signalingParameters = null;
		scalingType = ScalingType.SCALE_ASPECT_FILL;

		// Create UI controls.
		videoView = (GLSurfaceView) findViewById(R.id.glview_call);
		callFragment = new CallFragment();
		hudFragment = new HudFragment();

		// Create video renderers.
		VideoRendererGui.setView(videoView, new Runnable() {
			@Override
			public void run() {
				createPeerConnectionFactory();
			}
		});
		remoteRender = VideoRendererGui.create(ExtraVideoConstant.REMOTE_X,
				ExtraVideoConstant.REMOTE_Y, ExtraVideoConstant.REMOTE_WIDTH,
				ExtraVideoConstant.REMOTE_HEIGHT, scalingType, false);
		localRender = VideoRendererGui.create(
				ExtraVideoConstant.LOCAL_X_CONNECTING,
				ExtraVideoConstant.LOCAL_Y_CONNECTING,
				ExtraVideoConstant.LOCAL_WIDTH_CONNECTING,
				ExtraVideoConstant.LOCAL_HEIGHT_CONNECTING, scalingType, true);

		// Show/hide call control fragment on view click.
		videoView.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				toggleCallControlFragmentVisibility();
			}
		});

		// Check for mandatory permissions.
		for (String permission : ExtraVideoConstant.MANDATORY_PERMISSIONS) {
			if (checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
				logAndToast("Permission " + permission + " is not granted");
				setResult(RESULT_CANCELED);
				finish();
				return;
			}
		}

		// Get Intent parameters.
		final Intent intent = getIntent();
		if (intent != null) {
			Bundle bundle = intent.getExtras();
			mQyxMsg = bundle.getParcelable("message");
		}
		initView();
		Uri roomUri = intent.getData();
		if (roomUri == null) {
			logAndToast(getString(R.string.missing_url));
			Log.e(TAG, "Didn't get any URL in intent!");
			setResult(RESULT_CANCELED);
			finish();
			return;
		}
		String roomId = intent.getStringExtra(ExtraVideoConstant.EXTRA_ROOMID);
		if (roomId == null || roomId.length() == 0) {
			logAndToast(getString(R.string.missing_url));
			Log.e(TAG, "Incorrect room ID in intent!");
			setResult(RESULT_CANCELED);
			finish();
			return;
		}
		boolean loopback = intent.getBooleanExtra(
				ExtraVideoConstant.EXTRA_LOOPBACK, false);
		peerConnectionParameters = new PeerConnectionParameters(
				intent.getBooleanExtra(ExtraVideoConstant.EXTRA_VIDEO_CALL,
						true), loopback, intent.getIntExtra(
						ExtraVideoConstant.EXTRA_VIDEO_WIDTH, 0),
				intent.getIntExtra(ExtraVideoConstant.EXTRA_VIDEO_HEIGHT, 0),
				intent.getIntExtra(ExtraVideoConstant.EXTRA_VIDEO_FPS, 0),
				intent.getIntExtra(ExtraVideoConstant.EXTRA_VIDEO_BITRATE, 0),
				intent.getStringExtra(ExtraVideoConstant.EXTRA_VIDEOCODEC),
				intent.getBooleanExtra(
						ExtraVideoConstant.EXTRA_HWCODEC_ENABLED, true),
				intent.getIntExtra(ExtraVideoConstant.EXTRA_AUDIO_BITRATE, 0),
				intent.getStringExtra(ExtraVideoConstant.EXTRA_AUDIOCODEC),
				intent.getBooleanExtra(
						ExtraVideoConstant.EXTRA_NOAUDIOPROCESSING_ENABLED,
						false), intent.getBooleanExtra(
						ExtraVideoConstant.EXTRA_CPUOVERUSE_DETECTION, true));
		commandLineRun = intent.getBooleanExtra(
				ExtraVideoConstant.EXTRA_CMDLINE, false);
		runTimeMs = intent.getIntExtra(ExtraVideoConstant.EXTRA_RUNTIME, 0);

		// Create connection client and connection parameters.
		appRtcClient = new WebSocketRTCClient(this, new LooperExecutor());
		roomConnectionParameters = new RoomConnectionParameters(
				roomUri.toString(), roomId, loopback);

		// Send intent arguments to fragments.
		callFragment.setArguments(intent.getExtras());
		hudFragment.setArguments(intent.getExtras());
		// Activate call and HUD fragments and start the call.
		FragmentTransaction ft = getFragmentManager().beginTransaction();
		ft.add(R.id.call_fragment_container, callFragment);
		ft.add(R.id.hud_fragment_container, hudFragment);
		ft.commit();
		startCall();

		// For command line execution run connection for <runTimeMs> and exit.
		if (commandLineRun && runTimeMs > 0) {
			videoView.postDelayed(new Runnable() {
				public void run() {
					disconnect();
				}
			}, runTimeMs);
		}
	}

	/**
	 * 初始化View
	 */
	private void initView() {
		top_view = (RelativeLayout) findViewById(R.id.top_view);

		call_avatar_iv = (RoundImageView) findViewById(R.id.call_avatar_iv);
		call_name_tv = (TextView) findViewById(R.id.call_name_tv);
		call_time_tv = (TextView) findViewById(R.id.call_time_tv);
		if (mQyxMsg != null) {
			call_avatar_iv.SetUrl(APIConfiguration.getAvatarUrlNormal(
					mQyxMsg.to_cust_id + "", PacketType.PERSONAL));
			call_name_tv.setText(mQyxMsg.to_cust_name);
			/** 请求信息 **/
			if (mQyxMsg.to_cust_id == Long.valueOf(IMApplication.getCustId())) {
				top_view.setVisibility(View.GONE);
			} else {
				playSound("phonering.mp3", true);
			}
		}
	}

	// Activity interfaces
	@Override
	public void onPause() {
		super.onPause();
		videoView.onPause();
		activityRunning = false;
		if (peerConnectionClient != null) {
			peerConnectionClient.stopVideoSource();
		}
	}

	@Override
	public void onResume() {
		super.onResume();
		videoView.onResume();
		activityRunning = true;
		if (peerConnectionClient != null) {
			peerConnectionClient.startVideoSource();
		}

		if (mRefuseVideoChatBroadcastReceiver == null) {
			mRefuseVideoChatBroadcastReceiver = new RefuseVideoChatBroadcastReceiver();
			IntentFilter filter = new IntentFilter();
			filter.addAction(BroadcastAction.ACTION_REFUSE_VIDEO_CHAT);
			registerReceiver(mRefuseVideoChatBroadcastReceiver, filter);
		}
	}

	@Override
	protected void onDestroy() {
		disconnect();
		super.onDestroy();
		if (logToast != null) {
			logToast.cancel();
		}
		if (BackgroundMediaPlayer != null) {
			BackgroundMediaPlayer.release();
			BackgroundMediaPlayer = null;
		}
		if (callTimer != null) {
			callTimer.cancel();
			callTimer.purge();
			callTimer = null;
		}
		activityRunning = false;
		VideoRendererGui.dispose();
		if (mRefuseVideoChatBroadcastReceiver != null) {
			unregisterReceiver(mRefuseVideoChatBroadcastReceiver);
		}
		IMApplication.getInstance().removeActivity(this);
	}

	// CallFragment.OnCallEvents interface implementation.
	@Override
	public void onCallHangUp() {
		if (iceConnected) {
			/** 挂断视频通话 **/
			// TODO 修改通话时间
//			mTalkMsgManager.updateFieldMsgByMsgNo(
//					DBTalkMsgColumns.DURATION_TIME, talk_time + "",
//					mQyxMsg.msg_no);
		} else {
			/** 取消呼叫 **/
			// TODO
			MsgCancelCallVideoModel callVideoModel = new MsgCancelCallVideoModel();
			callVideoModel.setCancelCallVideoMsgNo(mQyxMsg.msg_no);
			callVideoModel.setFromCustName(IMApplication.getCustName());
			SessionModel sessionModel = new SessionModel();
			sessionModel.setSessionId(mQyxMsg.to_cust_id);
			sessionModel.setSessionType(PacketType.PERSONAL);
			IMApplication.getInstance().sendMessage(
					callVideoModel, sessionModel, null);

			mQyxMsg.msg_content_type = MsgContentType.CANCEL_CALL_VIDEO_CHAT;
			mQyxMsg.msg_type = MsgType.MSG_SENDS + "";
			mQyxMsg.content = mQyxMsg.msg_no;
			mQyxMsg.msg_no = mQyxMsg.getMsgNo(mQyxMsg.from_cust_id + "",
					mQyxMsg.to_cust_id + "",
					mQyxMsg.sessionModel.getSessionId() + "");
			/** 将拒绝视频请求的消息发给对方 **/
			// TODO IMApplication.getInstance().sendMessage(mQyxMsg,null);
			/** 取消呼叫，修改聊天状态 **/
			Intent intent = new Intent(
					BroadcastAction.ACTION_CANCEL_CALL_VIDEO_CHAT);
			intent.putExtra("msg_no", mQyxMsg.content);
			sendBroadcast(intent);
			String content_json = mQyxMsg.content_json;
			if(!TextUtils.isEmpty(content_json)){
				try {
					JSONObject jsonObject = new JSONObject(content_json);
					if(jsonObject.has("video_chat_result")){
						jsonObject.remove("video_chat_result");
					}
					jsonObject.put("video_chat_result", VideoChatResultType.VideoChatResultTypeCancelCall);

					/** 更改数据库 **/
					mTalkMsgManager.updateFieldMsgByMsgNo(
							DBTalkMsgColumns.CONTENT_JSON,
							jsonObject.toString(),
							mQyxMsg.content);

				} catch (JSONException e) {
					e.printStackTrace();
				}
			}
		}
		disconnect();
	}

	@Override
	public void onCameraSwitch() {
		if (peerConnectionClient != null) {
			peerConnectionClient.switchCamera();
		}
	}

	@Override
	public void onVideoScalingSwitch(ScalingType scalingType) {
		this.scalingType = scalingType;
		updateVideoView();
	}

	// Helper functions.
	@SuppressLint("NewApi")
	private void toggleCallControlFragmentVisibility() {
		if (!iceConnected || !callFragment.isAdded()) {
			return;
		}
		// Show/hide call control fragment
		callControlFragmentVisible = !callControlFragmentVisible;
		FragmentTransaction ft = getFragmentManager().beginTransaction();
		if (callControlFragmentVisible) {
			ft.show(callFragment);
			ft.show(hudFragment);
		} else {
			ft.hide(callFragment);
			ft.hide(hudFragment);
		}
		ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
		ft.commit();
	}

	private void updateVideoView() {
		VideoRendererGui.update(remoteRender, ExtraVideoConstant.REMOTE_X,
				ExtraVideoConstant.REMOTE_Y, ExtraVideoConstant.REMOTE_WIDTH,
				ExtraVideoConstant.REMOTE_HEIGHT, scalingType, false);
		if (iceConnected) {
			VideoRendererGui.update(localRender,
					ExtraVideoConstant.LOCAL_X_CONNECTED,
					ExtraVideoConstant.LOCAL_Y_CONNECTED,
					ExtraVideoConstant.LOCAL_WIDTH_CONNECTED,
					ExtraVideoConstant.LOCAL_HEIGHT_CONNECTED,
					ScalingType.SCALE_ASPECT_FIT, true);
		} else {
			VideoRendererGui.update(localRender,
					ExtraVideoConstant.LOCAL_X_CONNECTING,
					ExtraVideoConstant.LOCAL_Y_CONNECTING,
					ExtraVideoConstant.LOCAL_WIDTH_CONNECTING,
					ExtraVideoConstant.LOCAL_HEIGHT_CONNECTING, scalingType,
					true);
		}
	}

	private void startCall() {
		if (appRtcClient == null) {
			Log.e(TAG, "AppRTC client is not allocated for a call.");
			return;
		}
		callStartedTimeMs = System.currentTimeMillis();

		// Start room connection.
		logAndToast(getString(R.string.connecting_to,
				roomConnectionParameters.roomUrl));
		appRtcClient.connectToRoom(roomConnectionParameters);

		// Create and audio manager that will take care of audio routing,
		// audio modes, audio device enumeration etc.
		audioManager = AppRTCAudioManager.create(this, new Runnable() {
			// This method will be called each time the audio state (number and
			// type of devices) has been changed.
			@Override
			public void run() {
				onAudioManagerChangedState();
			}
		});
		// Store existing audio settings and change audio mode to
		// MODE_IN_COMMUNICATION for best possible VoIP performance.
		Log.d(TAG, "Initializing the audio manager...");
		audioManager.init();
	}

	// Should be called from UI thread
	private void callConnected() {
		final long delta = System.currentTimeMillis() - callStartedTimeMs;
		Log.i(TAG, "Call connected: delay=" + delta + "ms");
		if (peerConnectionClient == null || isError) {
			Log.w(TAG, "Call is connected in closed or error state");
			return;
		}
		// Update video view.
		updateVideoView();
		// Enable statistics callback.
		peerConnectionClient.enableStatsEvents(true,
				ExtraVideoConstant.STAT_CALLBACK_PERIOD);
	}

	private void onAudioManagerChangedState() {
		// TODO(henrika): disable video if
		// AppRTCAudioManager.AudioDevice.EARPIECE
		// is active.
	}

	// Create peer connection factory when EGL context is ready.
	private void createPeerConnectionFactory() {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (peerConnectionClient == null) {
					final long delta = System.currentTimeMillis()
							- callStartedTimeMs;
					Log.d(TAG, "Creating peer connection factory, delay="
							+ delta + "ms");
					peerConnectionClient = PeerConnectionClient.getInstance();
					peerConnectionClient.createPeerConnectionFactory(
							CallActivity.this, peerConnectionParameters,
							CallActivity.this);
				}
				if (signalingParameters != null) {
					Log.w(TAG, "EGL context is ready after room connection.");
					onConnectedToRoomInternal(signalingParameters);
				}
			}
		});
	}

	// Disconnect from remote resources, dispose of local resources, and exit.
	private void disconnect() {
		activityRunning = false;
		if (appRtcClient != null) {
			appRtcClient.disconnectFromRoom();
			appRtcClient = null;
		}
		if (peerConnectionClient != null) {
			peerConnectionClient.close();
			peerConnectionClient = null;
		}
		if (audioManager != null) {
			audioManager.close();
			audioManager = null;
		}
		if (iceConnected && !isError) {
			setResult(RESULT_OK);
		} else {
			setResult(RESULT_CANCELED);
		}
		finish();
	}

	/**
	 * �Ͽ�����
	 *
	 * @param errorMessage
	 */
	private void disconnectWithErrorMessage(final String errorMessage) {
		if (commandLineRun || !activityRunning) {
			Log.e(TAG, "Critical error: " + errorMessage);
			disconnect();
		} else {
			DialogUtility.showSingleBtnDialog(CallActivity.this, errorMessage,
					R.string.sure, false, new IOnBottomDialogListener() {

						@Override
						public void onClicked() {
							disconnect();
						}
					});
		}
	}

	// Log |msg| and Toast about it.
	private void logAndToast(String msg) {
		Log.d(TAG, msg);
		if (logToast != null) {
			logToast.cancel();
		}
		logToast = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
		logToast.show();
	}

	private void reportError(final String description) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (!isError) {
					isError = true;
					disconnectWithErrorMessage(description);
				}
			}
		});
	}

	// -----Implementation of AppRTCClient.AppRTCSignalingEvents ---------------
	// All callbacks are invoked from websocket signaling looper thread and
	// are routed to UI thread.
	private void onConnectedToRoomInternal(final SignalingParameters params) {
		final long delta = System.currentTimeMillis() - callStartedTimeMs;

		signalingParameters = params;
		if (peerConnectionClient == null) {
			Log.w(TAG, "Room is connected, but EGL context is not ready yet.");
			return;
		}
		logAndToast("Creating peer connection, delay=" + delta + "ms");
		peerConnectionClient.createPeerConnection(
				VideoRendererGui.getEGLContext(), localRender, remoteRender,
				signalingParameters);

		if (signalingParameters.initiator) {
			logAndToast("Creating OFFER...");
			// Create offer. Offer SDP will be sent to answering client in
			// PeerConnectionEvents.onLocalDescription event.
			peerConnectionClient.createOffer();
		} else {
			if (params.offerSdp != null) {
				peerConnectionClient.setRemoteDescription(params.offerSdp);
				logAndToast("Creating ANSWER...");
				// Create answer. Answer SDP will be sent to offering client in
				// PeerConnectionEvents.onLocalDescription event.
				peerConnectionClient.createAnswer();
			}
			if (params.iceCandidates != null) {
				// Add remote ICE candidates from room.
				for (IceCandidate iceCandidate : params.iceCandidates) {
					peerConnectionClient.addRemoteIceCandidate(iceCandidate);
				}
			}
		}
	}

	@Override
	public void onConnectedToRoom(final SignalingParameters params) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				onConnectedToRoomInternal(params);
			}
		});
	}

	@Override
	public void onRemoteDescription(final SessionDescription sdp) {
		final long delta = System.currentTimeMillis() - callStartedTimeMs;
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (peerConnectionClient == null) {
					Log.e(TAG,
							"Received remote SDP for non-initilized peer connection.");
					return;
				}
				logAndToast("Received remote " + sdp.type + ", delay=" + delta
						+ "ms");
				peerConnectionClient.setRemoteDescription(sdp);
				if (!signalingParameters.initiator) {
					logAndToast("Creating ANSWER...");
					// Create answer. Answer SDP will be sent to offering client
					// in
					// PeerConnectionEvents.onLocalDescription event.
					peerConnectionClient.createAnswer();
				}
			}
		});
	}

	@Override
	public void onRemoteIceCandidate(final IceCandidate candidate) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (peerConnectionClient == null) {
					Log.e(TAG,
							"Received ICE candidate for non-initilized peer connection.");
					return;
				}
				peerConnectionClient.addRemoteIceCandidate(candidate);
			}
		});
	}

	@Override
	public void onChannelClose() {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				logAndToast("Remote end hung up; dropping PeerConnection");
				disconnect();
			}
		});
	}

	@Override
	public void onChannelError(final String description) {
		reportError(description);
	}

	// -----Implementation of
	// PeerConnectionClient.PeerConnectionEvents.---------
	// Send local peer connection SDP and ICE candidates to remote party.
	// All callbacks are invoked from peer connection client looper thread and
	// are routed to UI thread.
	@Override
	public void onLocalDescription(final SessionDescription sdp) {
		final long delta = System.currentTimeMillis() - callStartedTimeMs;
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (appRtcClient != null) {
					logAndToast("Sending " + sdp.type + ", delay=" + delta
							+ "ms");
					if (signalingParameters.initiator) {
						appRtcClient.sendOfferSdp(sdp);
					} else {
						appRtcClient.sendAnswerSdp(sdp);
					}
				}
			}
		});
	}

	@Override
	public void onIceCandidate(final IceCandidate candidate) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (appRtcClient != null) {
					appRtcClient.sendLocalIceCandidate(candidate);
				}
			}
		});
	}

	@Override
	public void onIceConnected() {
		updateViewHandler.sendEmptyMessage(1);
		startCallTime();
		final long delta = System.currentTimeMillis() - callStartedTimeMs;
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				logAndToast("ICE connected, delay=" + delta + "ms");
				iceConnected = true;
				callConnected();
			}
		});
	}

	@Override
	public void onIceDisconnected() {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				logAndToast("ICE disconnected");
				iceConnected = false;
				disconnect();
			}
		});
	}

	@Override
	public void onPeerConnectionClosed() {
	}

	@Override
	public void onPeerConnectionStatsReady(final StatsReport[] reports) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				if (!isError && iceConnected) {
					hudFragment.updateEncoderStatistics(reports);
				}
			}
		});
	}

	@Override
	public void onPeerConnectionError(final String description) {
		reportError(description);
	}

	/**
	 *
	 * @author SL
	 *
	 */
	private class RefuseVideoChatBroadcastReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context arg0, Intent arg1) {
			if (arg1.getAction().equals(
					BroadcastAction.ACTION_REFUSE_VIDEO_CHAT)) {
				String msg_no = arg1.getStringExtra("msg_no");
				int result_type = arg1.getIntExtra("video_chat_result_type",
						VideoChatResultType.VideoChatResltTypeNormal);
				if (!TextUtils.isEmpty(msg_no)) {
//					mTalkMsgManager.updateFieldMsgByMsgNo(
//							DBTalkMsgColumns.VIDEO_CHAT_RESULT, result_type
//									+ "", msg_no);
				}
				if (result_type == VideoChatResultType.VideoChatResultTypeBusy) {
					DialogUtility.showSingleBtnDialog(CallActivity.this,
							R.string.busy_line_other_side, R.string.sure,
							false, new IOnBottomDialogListener() {

								@Override
								public void onClicked() {
									disconnect();
								}
							});
					IMApplication.showToast(getResources().getString(
							R.string.busy_line_other_side_call_video_chat));
				} else {
					disconnect();
				}
			}
		}

	}

	/**
	 *
	 * @param name
	 * @param isLoop
	 */
	private void playSound(String name, boolean isLoop) {
		if (BackgroundMediaPlayer == null) {
			BackgroundMediaPlayer = new MediaPlayer();
		}
		if (BackgroundMediaPlayer != null) {
			BackgroundMediaPlayer.stop();
			BackgroundMediaPlayer.reset();
			BackgroundMediaPlayer.setLooping(isLoop);

			AssetManager am = getAssets();// ��ø�Ӧ�õ�AssetManager
			try {
				AssetFileDescriptor afd = am.openFd(name);
				BackgroundMediaPlayer.setDataSource(afd.getFileDescriptor(),
						afd.getStartOffset(), afd.getLength());
				BackgroundMediaPlayer.prepare();
				BackgroundMediaPlayer.start();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private void startCallTime() {
		callTimer = new Timer();
		callTimer.schedule(new TimerTask() {

			@Override
			public void run() {
				talk_time++;
				if (second >= 59) {
					minute++;
				}
				if (minute >= 59) {
					hour++;
				}
				second++;
				callTimeHandler.sendEmptyMessage(1);
			}
		}, 1000, 0);
	}

	private Handler callTimeHandler = new Handler(new Handler.Callback() {

		@Override
		public boolean handleMessage(Message msg) {

			if (msg.what == 1) {
				String mi = "", se = "", ho = "00";
				if (minute < 10) {
					mi = "0" + minute;
				} else {
					mi = minute + "";
				}

				if (second < 10) {
					se = "0" + second;
				} else {
					se = second + "";
				}
				if (hour > 0) {
					ho = hour + "";
				}
				call_time_tv.setText(ho + ":" + mi + ":" + se);
			}
			return false;
		}
	});

	private Handler updateViewHandler = new Handler(new Handler.Callback() {
		@Override
		public boolean handleMessage(Message msg) {
			if (msg.what == 1) {
				top_view.setVisibility(View.GONE);
				if (BackgroundMediaPlayer != null
						&& BackgroundMediaPlayer.isPlaying()) {
					BackgroundMediaPlayer.pause();
				}
			}
			return false;
		}
	});
}
